home *** CD-ROM | disk | FTP | other *** search
- Path: xanth!nic.MR.NET!hal!ncoast!allbery
- From: zeeff@b-tech.ann-arbor.mi.us.UUCP (Jon Zeeff)
- Newsgroups: comp.sources.misc
- Subject: v04i130: lmail, a local mail delivery program
- Keywords: lmail, smail
- Message-ID: <4875@b-tech.ann-arbor.mi.us>
- Date: 22 Oct 88 01:22:17 GMT
- Sender: allbery@ncoast.UUCP
- Reply-To: zeeff@b-tech.ann-arbor.mi.us.UUCP (Jon Zeeff)
- Organization: Branch Technology, Ann Arbor, MI
- Lines: 666
- Approved: allbery@ncoast.UUCP
-
- Posting-number: Volume 4, Issue 130
- Submitted-by: "Jon Zeeff" <zeeff@b-tech.ann-arbor.mi.us.UUCP>
- Archive-name: lmail
-
- Too many people are requesting this so I guess it should go to
- comp.sources.misc.
-
- This program is a local mail delivery agent. It's primary use is to
- add piping to files and programs for sites running smail2.5. Make sure that
- it does locking the way you want it to.
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of shell archive."
- # Contents: lmail.c
- # Wrapped by zeeff@b-tech on Mon Oct 17 10:26:40 1988
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f lmail.c -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"lmail.c\"
- else
- echo shar: Extracting \"lmail.c\" \(15989 characters\)
- sed "s/^X//" >lmail.c <<'END_OF_lmail.c'
- X/* #ident "@(#)lmail.c 1.1.1.1 88/09/09 15:07:52 " */
- X/*
- X * lmail replacement V 2.7
- X *
- X * Copyright 1988 Jon Zeeff (umix!b-tech!zeeff)
- X * Updated by "Greg A. Woods" <umix!ixpierre!woods>
- X *
- X * Permission is granted to use this in any manner provided that
- X * 1) the copyright notice is left intact, 2) you don't hold me
- X * responsible for any bugs and 3) you mail me any improvements that you
- X * make.
- X *
- X * This program can be used with smail as the local delivery agent (lmail).
- X * It's primary benefit is that it allows forwarding to programs and files.
- X * It also allows undeliverable local mail to be saved.
- X *
- X * Caution: I wrote this for my own use and it does what I want. I
- X * haven't looked into all portability and security issues nor is the
- X * code as clean as I would like. Use at your own risk.
- X *
- X * Note that a .fwd file in /usr/spool/uucppublic is ignored since
- X * it is usually publically writable. If you have other publically
- X * writable home directories, you need to change this program to also
- X * exclude these other directories.
- X *
- X * This program should be:
- X *
- X * -rws--x--x 1 root mail 19030 Sep 14 12:11 /bin/lmail
- X *
- X * You need to create an empty file /usr/mail/.lock
- X * and lmail-aliases. Aliases should contain the name
- X * you want aliased followed by new name(s). Eg.
- X *
- X * postmaster sam "|/usr/postmaster/postsaver -c"
- X *
- X * will cause mail to postmaster to go to sam and be piped into the
- X * program postsaver with the -c option. Both will be done while suid
- X * MAILMAN.
- X *
- X * Users can also forward mail with a .fwd file in their home
- X * directory. This contains just new names. References to files and
- X * programs in user's .fwd file will be executed suid that user.
- X * This is a security hole if the directory is writable by others!
- X * Also be careful with .fwd files where several ids share a home
- X * directory.
- X *
- X * This program was written for a Sys V.2 or V.3 system running smail 2.5.
- X *
- X * Step by step installation:
- X *
- X * 1) Change MAILGID define if mail is not group 6.
- X * 2) Add mailman to /etc/passwd with no special uid or group.
- X * 3) Compile with 'cc lmail.c -s -O -o lmail'
- X * 4) Create empty files lmail-aliases and /usr/mail/.lock
- X * These should be rw group mail.
- X *
- X * 5) Install as /bin/lmail with suid root perms.
- X * 6) Test
- X */
- X
- X#include <stdio.h>
- X#include <limits.h>
- X#include <sys/types.h>
- X#include <pwd.h>
- X#include <grp.h>
- X#include <utmp.h>
- X#include <signal.h>
- X#include <ctype.h>
- X#include <time.h>
- X#include <sys/stat.h>
- X#include <setjmp.h>
- X#include <sys/utsname.h>
- X#include <string.h>
- X
- Xvoid exit();
- Xvoid _exit();
- Xunsigned int sleep();
- Xtime_t time();
- X
- X/*
- X * Change these defines to fit your needs
- X */
- X#define MAX_LINE 512 /* input line buffer size */
- X#define DEF_PATH "PATH=/bin:/usr/bin:/usr/lbin"
- X#define MAIL_TMPFILE "/tmp/rmXXXXXX"
- X#define ROOT "root"
- X#define DEF_IFS "IFS= \t\n"
- X#define DEF_SHELL "SHELL=/bin/sh"
- X#define FROM_LINE "From "
- X#define FROM_FMT "From %s %24.24s\n"
- X#define FROM_PREFX '>'
- X#define REMOTE_FROM " remote from "
- X#define ALIAS_FILE "/usr/local/lib/lmail-aliases"
- X#define MAIL_DIR "/usr/mail/"
- X#define LOCK_SUFF ".lock"
- X#define LOCK_FILE "/usr/mail/.lock"
- X#define FWD_LINE "Forward to "
- X#define FWD_FILE "/.fwd"
- X#define PUBDIR_FWD "/usr/spool/uucppublic/.fwd"
- X#define REMOTE_MAILER "/bin/rmail %s" /* %s will be replace by address */
- X#ifndef PATH_MAX
- X#define PATH_MAX 1024
- X#endif
- X
- X
- X
- X/*
- X * GID of mail for creating mail files in /usr/mail. It's faster to just type
- X * it in here, and it probably won't change anyway (we hope).
- X */
- X#define MAILGID 6
- X
- X/*
- X * MAILMAN should be a user id with no special permissions or files. Make
- X * sure you add a /etc/passwd entry for it, 'cause I'm going to check.
- X */
- X#define MAILMAN "mailman" /* "mailman" */
- X
- X/*
- X * BADMAIL is a name to send a copy of bad mail to. Can be left undefined.
- X */
- X#define BADMAIL "/usr/mail/badmail" /* "/usr/mail/badmail" */
- X
- X/*
- X * Maximum size of aliasing table (NOTE: this uses a lot of space!)
- X */
- X#define MAX_ADDR 50
- X
- Xstruct {
- X char dest[MAX_LINE]; /* to whom it should go */
- X char source[MAX_LINE]; /* who it is from */
- X char user[MAX_LINE]; /* user to suid to for '|' and files */
- X} table[MAX_ADDR];
- X
- Xchar *thissys; /* This system's name */
- XFILE *mailfile; /* FILE pointer for mailbox */
- Xint num_addresses;
- Xlong iop;
- Xstruct utsname utsn;
- Xstruct passwd *pwd;
- Xstruct passwd *pwd_mailman;
- X
- Xstruct passwd *getpwnam();
- XFILE *fopen();
- Xunsigned short getuid();
- Xchar *mktemp();
- XFILE *popen();
- X
- Xvoid alias();
- Xvoid copy();
- Xvoid lock();
- Xvoid unlock();
- X
- X/* ARGSUSED */
- Xint
- Xmain(argc, argv, envp)
- X int argc;
- X char *argv[];
- X char *envp[];
- X{
- X char *address;
- X char from[MAX_LINE]; /* Original author */
- X char line[MAX_LINE];
- X int i;
- X int error_flag = 0;
- X char *ptr;
- X
- X static char tempfname[] = MAIL_TMPFILE;
- X
- X if (argc < 2)
- X exit(1); /* return error if not at least 1 address */
- X umask(006); /* mail files MUST be group write-able */
- X uname(&utsn);
- X /*
- X * If being fed from a pipe, copy to a temp file so we can rewind
- X */
- X if (fseek(stdin, 0L, 0) != 0) {
- X mailfile = fopen(mktemp(tempfname), "w+");
- X unlink(tempfname); /* it'll be gone when we are */
- X if (mailfile == NULL) {
- X (void) fprintf(stderr, "Can't create temp file - no mail delivered\n");
- X exit(3);
- X }
- X while (fgets(line, MAX_LINE, stdin) != NULL) {
- X if (fputs(line, mailfile) == EOF) {
- X (void) fprintf(stderr, "Can't write temp file - no mail delivered\n");
- X exit(3);
- X }
- X }
- X rewind(mailfile);
- X } else
- X mailfile = stdin;
- X thissys = utsn.nodename;
- X if ((pwd_mailman = getpwnam(MAILMAN)) == NULL) {
- X (void) fprintf(stderr, "*** Error - can't find mailman uid\n");
- X exit(7);
- X }
- X time(&iop);
- X putenv(DEF_PATH);
- X putenv(DEF_IFS);
- X putenv(DEF_SHELL);
- X /*
- X * Get the From_ line for the author - assume smail has folded it
- X */
- X from[0] = '\0';
- X fgets(line, MAX_LINE, mailfile);
- X rewind(mailfile);
- X if (strncmp(line, FROM_LINE, sizeof(FROM_LINE) - 1) != 0) {
- X (void) fprintf(stderr, "*** Error - mail is in incorrect format\n");
- X exit(2);
- X }
- X /*
- X * If remote from exists, then include that site name in address so that
- X * there is no remote from on the end
- X */
- X ptr = strrchr(line, ' ') - 1;
- X while (*ptr != ' ')
- X --ptr;
- X --ptr;
- X while (*ptr != ' ')
- X --ptr;
- X if (strncmp(ptr, REMOTE_FROM, sizeof(REMOTE_FROM) - 1) == 0) {
- X (void) strcat(from, strrchr(line, ' ') + 1);
- X *(strrchr(from, '\n')) = '!';
- X }
- X (void) strcat(from, strchr(line, ' ') + 1);
- X *(strchr(from, ' ')) = '\0';
- X /*
- X * Put the first entrys into the aliasing table
- X */
- X --argc;
- X for (num_addresses = 0; num_addresses < argc; ++num_addresses) {
- X address = argv[num_addresses+1];
- X if (ptr = strchr(address, ' '))
- X *ptr = '\0'; /* Remove trailing spaces */
- X /*
- X * Mailing to file or program is illegal at this point We
- X * can't have outsiders mailing directly to files
- X */
- X if (address[0] == '/' || address[0] == '|')
- X exit(1);
- X (void) strcpy(table[num_addresses].dest, address);
- X (void) strcpy(table[num_addresses].source, from);
- X (void) strcpy(table[num_addresses].user, MAILMAN);
- X }
- X alias(); /* expand address recursively */
- X signal(SIGHUP, SIG_IGN);
- X signal(SIGINT, SIG_IGN);
- X signal(SIGQUIT, SIG_IGN);
- X /*
- X * Now deliver them
- X */
- X for (i = 0; i < num_addresses; i++) {
- X if (*(table[i].source) != '\0') /* if not deleted */
- X error_flag |= deliver(mailfile, from, table[i].dest, table[i].user);
- X }
- X#ifdef BADMAIL
- X if (error_flag)
- X deliver(mailfile, from, BADMAIL, ROOT);
- X#endif
- X /*
- X * Realize here that if there is any kind of error, smail will return
- X * mail to the author. Even if the error was on the part of some
- X * local user who did an alias wrong.
- X */
- X return(error_flag);
- X}
- X
- X
- X/*
- X * This routine recursively aliases an address creating a table of addresses.
- X */
- Xvoid
- Xalias()
- X{
- X FILE *in_file;
- X int i;
- X int j;
- X char *p;
- X char *p2;
- X char owner[MAX_LINE];
- X char file[PATH_MAX];
- X char line[MAX_LINE];
- X FILE *aliases;
- X struct stat statbuf;
- X
- X if ((aliases = fopen(ALIAS_FILE, "r")) == (FILE *) NULL)
- X return;
- X /*
- X * Make sure someone didn't break mail and link some file to aliases.
- X * Sys V doesn't have sym links, so we don't worry about that.
- X */
- X fstat(fileno(aliases), &statbuf);
- X if (statbuf.st_nlink > 1) {
- X fclose(aliases);
- X return;
- X }
- X for (i = 0; i < num_addresses; ++i) {
- X /*
- X * Only alias it if it has never been seen before
- X */
- X for (j = 0; j < i; ++j) {
- X if (strcmp(table[j].dest, table[i].dest) == 0)
- X break;
- X }
- X if (j < i)
- X continue;
- X /*
- X * find a matching line in aliases
- X */
- X rewind(aliases);
- X while (fscanf(aliases, "%s %s", file, line) > 0) {
- X if (strcmp(file, table[i].dest) == 0)
- X break;
- X }
- X if (!feof(aliases)) { /* we found one in aliases */
- X p = line;
- X (void) strcpy(owner, MAILMAN);
- X } else {
- X /*
- X * try the users .fwd file. This is preferred over
- X * Forward to lines
- X */
- X if ((pwd = getpwnam(table[i].dest)) == NULL)
- X continue;
- X (void) strcpy(file, pwd->pw_dir);
- X (void) strcat(file, FWD_FILE);
- X /*
- X * /usr/spool/uucppublic is normally a publically
- X * writable home directory. Ignore any .fwd there.
- X */
- X if (strcmp(file, PUBDIR_FWD) != 0 && (in_file = fopen(file, "r")) != NULL) {
- X /*
- X * ignore it if there is anything funny going
- X * on with links
- X */
- X fstat(fileno(in_file), &statbuf);
- X if (statbuf.st_nlink > 1 || fgets(line, MAX_LINE, in_file) == NULL) {
- X fclose(in_file);
- X continue;
- X }
- X fclose(in_file);
- X p = line;
- X (void) strcpy(owner, table[i].dest);
- X } else {
- X /*
- X * maybe they have a Forward to in their mail
- X * file
- X */
- X (void) strcpy(file, MAIL_DIR);
- X (void) strcat(file, table[i].dest);
- X if ((in_file = fopen(file, "r")) == (FILE *) NULL)
- X continue;
- X /*
- X * ignore it if there is anything funny going
- X * on with links
- X */
- X fstat(fileno(in_file), &statbuf);
- X if (statbuf.st_nlink > 1 || fgets(line, MAX_LINE, in_file) == (char *) NULL) {
- X fclose(in_file);
- X continue;
- X }
- X fclose(in_file);
- X if (strncmp(line, FWD_LINE, sizeof(FWD_LINE) - 1) != 0) continue;
- X p = line + 11;
- X /*
- X * we only allow simple forwards with this
- X * method this is overly simple
- X */
- X if (strchr(p, '/') || strchr(p, '|'))
- X continue;
- X (void) strcpy(owner, MAILMAN);
- X }
- X }
- X /*
- X * p now points to a line of addresses. Mark the current
- X * entry as deleted since it was just aliased.
- X */
- X *(table[i].source) = '\0';
- X while (*p > '\n') {
- X if (*p == '"') {
- X p2 = p + 1;
- X while (*(++p) && *p != '"')
- X ; /* NO_OP */
- X } else {
- X p2 = p;
- X while (*p > ' ')
- X ++p;
- X }
- X if (*p != '\0')
- X *(p++) = '\0';
- X /*
- X * Is an unaliased version of this already in the
- X * table?
- X */
- X for (j = 0; j < num_addresses; ++j) {
- X if (strcmp(table[j].dest, p2) == 0 && *(table[j].source) != '\0')
- X break;
- X }
- X /*
- X * Add new entry if it finished the above loop
- X */
- X if (j == num_addresses) {
- X (void) strcpy(table[num_addresses].dest, p2);
- X (void) strcpy(table[num_addresses].source, table[i].dest);
- X (void) strcpy(table[num_addresses].user, owner);
- X if (++num_addresses >= MAX_ADDR) {
- X --num_addresses;
- X return;
- X }
- X }
- X /*
- X * Move past spaces
- X */
- X while (*p == ' ')
- X ++p;
- X }
- X }
- X fclose(aliases);
- X return;
- X}
- X
- X/*
- X * This routine attempts to deliver the mail
- X */
- Xint
- Xdeliver(in_fd, author, dest_ptr, user)
- X FILE *in_fd; /* Input file */
- X char *author; /* Who message is from (with address) */
- X char *dest_ptr; /* Who message is to */
- X char *user; /* User responsible for this to address */
- X{
- X char dest[MAX_LINE];
- X char temp[MAX_LINE];
- X FILE *outfile;
- X int pid;
- X int w;
- X int status;
- X
- X rewind(in_fd);
- X (void) strcpy(dest, dest_ptr);
- X if ((strchr(dest, '!') || strchr(dest, '@') || strchr(dest, '%')) && dest[0] != '|' && dest[0] != '/') { /* A remote address */
- X /*
- X * fix things that have only a %
- X */
- X if (strchr(dest, '!') == (char *) NULL && strchr(dest, '@') == (char *) NULL)
- X *(strchr(dest, '%')) = '@';
- X sprintf(temp, REMOTE_MAILER, dest);
- X if ((pwd = getpwnam(user)) == (struct passwd *) NULL)
- X pwd = pwd_mailman;
- X if (pid = fork()) {
- X while ((w = wait(&status)) != pid && w != -1)
- X ; /* NO_OP */
- X if (status || w == -1) {
- X (void) fprintf(stderr, "\nCannot run %s\n", temp);
- X return(8);
- X }
- X } else {
- X setgid(pwd->pw_gid);
- X setuid(pwd->pw_uid);
- X umask(066);
- X if ((outfile = popen(temp, "w")) == (FILE *) NULL)
- X _exit(1);
- X copy(outfile, in_fd, author);
- X if (pclose(outfile))
- X _exit(1);
- X _exit(0);
- X }
- X } else {
- X if (dest[0] == '|') {
- X if ((pwd = getpwnam(user)) == (struct passwd *) NULL)
- X pwd = pwd_mailman;
- X if (pid = fork()) {
- X while ((w = wait(&status)) != pid && w != -1)
- X ;
- X if (status || w == -1) {
- X (void) fprintf(stderr, "\nCannot pipe to program %s\n", dest);
- X return(8);
- X }
- X } else {
- X setgid(pwd->pw_gid);
- X setuid(pwd->pw_uid);
- X umask(066);
- X if ((outfile = popen(dest + 1, "w")) == (FILE *) NULL)
- X _exit(1);
- X copy(outfile, in_fd, author);
- X if (pclose(outfile))
- X _exit(1);
- X _exit(0);
- X }
- X } else {
- X if (dest[0] == '/') {
- X if ((pwd = getpwnam(user)) == (struct passwd *) NULL)
- X pwd = pwd_mailman;
- X if (pid = fork()) {
- X while ((w = wait(&status)) != pid && w != -1)
- X ; /* NO_OP */
- X if (status || w == -1) {
- X (void) fprintf(stderr, "\nCannot save in file %s\n", dest);
- X return(8);
- X }
- X } else {
- X setgid(pwd->pw_gid);
- X setuid(pwd->pw_uid);
- X umask(066);
- X lock2(dest);
- X if ((outfile = fopen(dest, "a")) == (FILE *) NULL)
- X _exit(1);
- X copy(outfile, in_fd, author);
- X fclose(outfile);
- X unlock(dest);
- X _exit(0);
- X }
- X } else { /* a local user address */
- X /*
- X * Check if this is a valid user
- X */
- X if ((pwd = getpwnam(dest)) == NULL) {
- X (void) fprintf(stderr, "User %s does not exist\n", dest);
- X return(4);
- X }
- X if (pid = fork()) {
- X while ((w = wait(&status)) != pid && w != -1)
- X ;
- X if (status || w == -1) {
- X (void) fprintf(stderr, "\nCannot save in file %s\n", dest);
- X return(8);
- X }
- X } else {
- X setgid(MAILGID);
- X setuid(pwd_mailman->pw_uid); /* give up root uid */
- X sprintf(temp, "%s%s", MAIL_DIR, dest);
- X lock(temp);
- X status = 0;
- X if ((outfile = fopen(temp, "a")) == NULL) {
- X (void) fprintf(stderr, "** Can't open user mail file %s\n", temp);
- X status = 5;
- X } else
- X copy(outfile, in_fd, author);
- X chown(temp, (int)pwd->pw_uid, MAILGID);
- X if (fclose(outfile)) {
- X (void) fprintf(stderr, "** Could not close mail file %s\n", temp);
- X status = 7;
- X }
- X unlock(temp);
- X _exit(status);
- X }
- X }
- X }
- X }
- X return(0);
- X}
- X
- Xvoid
- Xcopy(out, in, from)
- X FILE *out;
- X FILE *in;
- X char *from;
- X{
- X char temp[MAX_LINE];
- X
- X /*
- X * Throw away From_ line and replace with our own
- X */
- X fgets(temp, MAX_LINE, in);
- X (void) fprintf(out, FROM_FMT, from, ctime(&iop));
- X while (fgets(temp, MAX_LINE, in) != NULL) {
- X if (strncmp(temp, FROM_LINE, sizeof(FROM_LINE) - 1) == 0)
- X (void) fputc(FROM_PREFX, out);
- X (void) fputs(temp, out);
- X }
- X (void) fputc('\n', out);
- X return;
- X}
- X
- X/*
- X * This routine works while uid = root
- X */
- Xvoid
- Xlock(file)
- X char *file;
- X{
- X char lockfile[PATH_MAX];
- X int i;
- X
- X (void) strcpy(lockfile, file);
- X (void) strcat(lockfile, LOCK_SUFF);
- X for (i = 0; i < 100; i++) {
- X if (link(LOCK_FILE, lockfile) == 0)
- X return;
- X sleep(5);
- X }
- X return;
- X}
- X
- X/*
- X * This routine is used for files in user's directories
- X */
- Xlock2(file)
- X char *file;
- X{
- X char lockfile[PATH_MAX];
- X int f;
- X int i;
- X
- X (void) strcpy(lockfile, file);
- X (void) strcat(lockfile, LOCK_SUFF);
- X for (i = 0; i < 100; i++) {
- X if ((f = creat(lockfile, 0)) >= 0) {
- X close(f);
- X return;
- X } else
- X sleep(5);
- X }
- X}
- X
- Xvoid
- Xunlock(file)
- X char *file;
- X{
- X char lockfile[PATH_MAX];
- X
- X (void) strcpy(lockfile, file);
- X (void) strcat(lockfile, LOCK_SUFF);
- X (void) unlink(lockfile);
- X return;
- X}
- X
- X
- END_OF_lmail.c
- if test 15989 -ne `wc -c <lmail.c`; then
- echo shar: \"lmail.c\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- echo shar: End of shell archive.
- exit 0
- --
- Jon Zeeff Branch Technology,
- umix!b-tech!zeeff zeeff@b-tech.ann-arbor.mi.us
-